import io
import logging

from django_redis import get_redis_connection
from django.http import HttpResponse
from django.views import View
from rest_framework_simplejwt.views import TokenObtainSlidingView
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet
# from rest_framework.mixins import RetrieveModelMixin  # 查单一
from rest_framework.mixins import *
from rest_framework_extensions.cache.mixins import CacheResponseMixin
from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR
from rest_framework.decorators import action
from fdfs_client.client import Fdfs_client, get_tracker_conf
from rest_framework.permissions import IsAuthenticated

from config.fastdfs.config import FASTDFS_SERVER_DOMAIN
from utils.verifyUtil import ImageVerify
from .models import Area
from .serializers import MyTokenSerializer, UserDetailSerializer
from config.dbs.redisconfig import LOGIN_VERIFY_CODE_TEMPLATE, EXPIRE_TIME
# from .serializers import User, UserSerializer
from .serializers import *
from utils.customPermissions import ActiveUserPermission, auto_user

# 导入日志器 __name__以当前文件的文件名作为日志器的名字
logger = logging.getLogger(__name__)

# fastdfs客户端
client_conf = get_tracker_conf('config/fastdfs/client.conf')
client = Fdfs_client(client_conf)


# Create your views here.
class MyToken(TokenObtainSlidingView):
    # 让TokenObtainSlidingView变成自己的
    serializer_class = MyTokenSerializer


# 不需要mysql所以不用ModelViewSet
class ImageVerifyView(View):
    def get(self, request, uuid):
        """
        获取图形验证码
        :param request:
        :return:
        """
        imgVerify = ImageVerify()
        img, code = imgVerify.image_code()

        # 图片已二进制方式进行传输
        # 创建一个字节流管道 bytes_box
        bytes_box = io.BytesIO()
        # 将图片以PNG个格式保存到字节流管道
        img.save(bytes_box, format='PNG')  # format='PNG' 保存文件格式
        # 从字节流管道获取到二进制的图片
        image_bytes = bytes_box.getvalue()

        # code保存到redis数据库
        # 连接到我们的redis
        cache = get_redis_connection(alias='verify_codes')  # alias指定哪个redis库
        cache.set(LOGIN_VERIFY_CODE_TEMPLATE % uuid, code, EXPIRE_TIME)  # key value  # uuid由前端传进 # EXPIRE_TIME过期时间

        return HttpResponse(image_bytes, content_type='image/png')


class UserViewSet(RetrieveModelMixin, GenericViewSet):
    queryset = User.objects.filter(is_active=1)  # 拿到我们的查询集
    serializer_class = UserSerializer  # 拿到我们写好的序列化器
    permission_classes = [IsAuthenticated]  # 导入登录用户只能操作当前的用户相关功能的权限

    def get_serializer(self, *args, **kwargs):
        if self.action == 'password':
            return UpdatePasswordSerializer(*args, **kwargs)
        return self.serializer_class(*args, **kwargs)

    def update(self, request, pk):
        """
        个人信息修改
        1.User表更新
        -通过pk查询user对象
        -拿到传入的数据request.data，反序列化
        -字段校验is_valid
        -更新
        2.UserDetail表更新
        -判断该用户是否有详情
        -有,则获取；无,则创建
        -字段校验
        -更新
        :param request:
        :param pk:
        :return:
        """
        # 1.User表更新
        # 通过pk查询user对象
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 拿到传入的数据request.data，反序列化
        user_serializer = self.get_serializer(user, data=request.data)
        # 字段校验is_valid
        user_serializer.is_valid(raise_exception=True)  # 校验错误抛出异常
        # 更新
        user_serializer.save()

        # 2.UserDetail表更新
        # 判断该用户是否有详情
        user_detail = request.data['userdetail']
        # 有,则获取；无,则创建
        if hasattr(user, 'userdetail'):
            # UserDetailSerializer序列化器  # user详情对象     # 刚刚获取到的request数据
            user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail)
        else:  # 无instence  直接前端去获取
            user_detail_serializer = UserDetailSerializer(data=user_detail)
        # 字段校验
        user_detail_serializer.is_valid(raise_exception=True)  # 有错误则抛出错误
        # 更新
        user_detail_serializer.save()  # 没有则保存

        # 构造返回的数据结构
        user_serializer.data['userdetail'] = user_detail_serializer.data
        return Response(user_serializer.data)

    @action(methods=['put'], detail=True)  # 有pk值detail针对某一个对象
    def password(self, request, pk):
        """
        1.根据pk值查找对象
        2.拿到传入的数据, request.data
        3.字段校验
        4.判断提交的旧密码和数据库里存放的是否一致
            -正确, 修改
            -错误, 返回状态码, 终止程序
        :param request:
        :param pk:
        :return:
        """
        # 根据pk值查找对象
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 拿到传入的数据, request.data
        password = request.data['password']
        new_password = request.data['new_password']
        # 字段校验
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 判断提交的旧密码和数据库里存放的是否一致
        if user.check_password(password):  # 因为数据库里的密码是密文的,所有需要用check方法来比较
            # -正确, 修改
            user.set_password(new_password)
            user.save()
            return Response({'msg': '修改成功'})
        # -错误, 返回状态码, 终止程序
        return Response(status=HTTP_400_BAD_REQUEST)

    @action(methods=['post'], detail=True)
    def avatar(self, request, pk):
        """
        头像上传功能
        1.根据pk获取用户对象
        2.获取传入的信息(头像图片文件, request.FILES)
        3.校验(文件信息和格式)
        4.上传到FastDFS
        5.将返回的地址存储到mysql数据库(详情表)
        6.返回响应
        :param request:
        :param pk:
        :return:
        """
        # 根据pk获取用户对象
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 获取传入的信息(头像图片文件, request.FILES)
        files = request.FILES.get('file')
        # 校验(文件信息和格式)
        if not files or files.content_type not in ('image/jpeg', 'image/png', 'image/gif', 'image/jpg'):
            return Response(status=HTTP_400_BAD_REQUEST)
        # 上传到FastDFS  # 1.png 拿取到后缀名
        try:
            image_ext_name = files.name.split('.')[-1]
        except Exception as e:
            image_ext_name = 'png'
        try:
            res = client.upload_by_buffer(files.read(), file_ext_name=image_ext_name)
        except Exception as e:
            logger.error(f'图片上传错误: {e}')
            return Response(status=HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            if res.get('Status') != 'Upload successed.':
                return Response({'msg': '图片上传失败,请稍后再尝试,或者联系管理员'}, status=HTTP_500_INTERNAL_SERVER_ERROR)
            else:
                # 将返回的地址存储到mysql数据库(详情表)
                image_url = FASTDFS_SERVER_DOMAIN + res.get('Remote file_id').decode()
                if hasattr(user, 'userdetail'):
                    # 有,则修改
                    user_detail = user.userdetail
                    user_detail.avatar = image_url
                    user_detail.save()
                else:
                    # 无,则创建
                    UserDetail.objects.create(avatar=image_url, user=user)  # 让avatar头像等于图片地址 user=user详情表用户表关联
                # 返回响应
                return Response({'data': image_url})


class AreaViewSet(CacheResponseMixin,
                  ReadOnlyModelViewSet):  # ReadOnlyModelViewSet是包括了GenericViewSet,ListModelMixin,RetrieveModelMixin的视图集
    # queryset = Area.objects.filter(parent=None)  # 查询没有上级行政区的
    # queryset是查询所有的省份,已经写死了,所以需要重写一下
    def get_queryset(self):
        if self.action == 'list':
            return Area.objects.filter(parent=None)  # 查询省份
        else:
            return Area.objects.all()  # 查询所有省份下的城市

    # serializer_class = AreaSerializer
    # 序列化器也写死了,当查省份下的城市时应该调用ParentSerializer序列化器而非AreaSerializer
    def get_serializer_class(self):
        if self.action == 'list':
            return AreaSerializer
        else:
            return ParentSerializer  # 查询所有省份下的城市

    permission_classes = [IsAuthenticated]


class AddressViewSet(ModelViewSet):  # 用户可以增删改查单一查全部收货地址
    # 查询集: 当前登录用户,只能查询直接的地址
    def get_queryset(self):
        return self.request.user.address_set.filter(is_delete=False)  # 通过用户去查询收货地址,反向查询

    serializer_class = AddressSerializer
    permission_classes = [IsAuthenticated]

    @auto_user  # 实现每个人得到的是自己的id,只能看自己的收货地址
    def create(self, request, *args, **kwargs):
        return CreateModelMixin.create(self, request, *args, **kwargs)

    @auto_user
    def update(self, request, *args, **kwargs):
        return UpdateModelMixin.update(self, request, *args, **kwargs)
